home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 March: Reference Library / Dev.CD Mar 97 RL.toast / mac / Technical Documentation / develop / develop Issue 28 / develop Issue 28 code / CurveLayout / AccurateShapesLibrary / AccuratePrimitiveShape.c next >
Encoding:
C/C++ Source or Header  |  1996-09-22  |  13.8 KB  |  504 lines  |  [TEXT/CWIE]

  1. #include "GXExceptions.h"
  2. #include "GXGraphics.h"
  3. #include "ComputeLength.h"    
  4. #include "PathWalking.h"
  5. #include "AccurateShapes.h"
  6.  
  7. /********************************
  8.  
  9.     ApplyOneBendDash:
  10.     
  11.     This is my own version of PrimitiveShape for the case of
  12.     a bend dash where the dash shape is a polygon.  (see rules below)
  13.     
  14.     This is because there seem to be inaccuracies in GX dashing for this
  15.     case which cause the text positioning to look bad when the dashing
  16.     is used to determine CurveLayout.
  17.     
  18.     Since We know the advance is zero, we only have to repeat the dash once.
  19.     This makes it easy.
  20.     
  21.  
  22. ********************************/
  23.  
  24. typedef struct {
  25.  
  26.     gxPoint        currentPoint;                            // the current point in our state.
  27.     gxPoint        firstPoint;                                // the first point in the current contour.
  28.     double        currentLength;                        // cache of current length;
  29.     long            currentDashSegment;                // index of current dash shape segment.
  30.     long            dashSegmentCount;                    // How many segmentsin the dash shape.
  31.     double        *startLengths;                        // Pointer to array of dash segment start lengths;
  32.     double        *endLengths;                            // Pointer to array of dash segment end lengths;
  33.     Boolean        startedSegment;                        // Have we started building a bent segment?
  34.     gxShape        bendDashShape;                        // Build the bend dash in here.    
  35.     
  36. } TBendDashRecord;
  37.  
  38.  
  39. void CurveToPathsStructure(gxCurve *subCurve, gxPaths *subPath);
  40. void CurveToPathsStructure(gxCurve *subCurve, gxPaths *subPath)
  41.     {
  42.         char        *pWalker = (char*)subPath;
  43.         long        *aLong;
  44.         gxPoint    *aPoint;
  45.         
  46.         aLong = (long*)pWalker;
  47.         pWalker += sizeof(long);
  48.         *aLong = 1;                                    // how many countours.
  49.     
  50.         aLong = (long*)pWalker;
  51.         pWalker += sizeof(long);
  52.         *aLong = 3;                                    // how many poins in the contour.    
  53.         
  54.         aLong = (long*)pWalker;
  55.         pWalker += sizeof(long);
  56.         *aLong = 0x40000000;                    // control bits: 0-1-0-...  for on off on.
  57.         
  58.         /* now the points */
  59.         
  60.         aPoint = (gxPoint*)pWalker;
  61.         aPoint->x = subCurve->first.x;
  62.         aPoint->y = subCurve->first.y;
  63.         aPoint += 1;
  64.         
  65.         aPoint->x = subCurve->control.x;
  66.         aPoint->y = subCurve->control.y;
  67.         aPoint += 1;
  68.         
  69.         aPoint->x = subCurve->last.x;
  70.         aPoint->y = subCurve->last.y;
  71.         
  72.     }//CurveToPathsStructure
  73.  
  74.  
  75. /*******
  76.  
  77.     PathWalker Callbacks for implementing bend dashing of hairlines:
  78.     
  79.     As we enter a line or a curve we look at the current dash segment's 
  80.     start and end length.  This information determines whether the current
  81.     shape's curve or line segment contains the dash.  It can contain all or
  82.     part of the dash.  In the case where it contains part of the dash, the part
  83.     is computed and added to the bendDash result.
  84.  
  85. ********/
  86.  
  87.  
  88. Boolean BendDashCurveto(gxPoint p[3],  TBendDashRecord *bdr);
  89. Boolean BendDashCurveto(gxPoint p[3],  TBendDashRecord *bdr)
  90.     {
  91.         gxCurve            aCurve, subCurve;
  92.         char                subPathData[ sizeof(long) + sizeof(long) + 3 * sizeof(gxPoint) + sizeof(long)]; 
  93.         gxPaths            *subPath = (gxPaths*)subPathData;
  94.         double            segmentLength;
  95.         double            d1, d2;
  96.         Boolean            inSegment = false;
  97.         Boolean            startContour = false;
  98.         Boolean            result = false;
  99.         
  100.         aCurve.first.x = p[0].x;
  101.         aCurve.first.y = p[0].y;
  102.         aCurve.control.x = p[1].x;
  103.         aCurve.control.y = p[1].y;
  104.         aCurve.last.x = p[2].x;
  105.         aCurve.last.y = p[2].y;
  106.         
  107.         segmentLength = GetCurveLength(&aCurve);
  108.         
  109.         if (!bdr->startedSegment) {
  110.         
  111.             // If we haven't started with this dash segment, see if we should start.
  112.             
  113.             if ( bdr->startLengths[bdr->currentDashSegment] <= (bdr->currentLength + segmentLength) ) {
  114.                 bdr->startedSegment = true;
  115.                 d1 = bdr->startLengths[bdr->currentDashSegment] - bdr->currentLength;
  116.                 inSegment = true;
  117.                 startContour = true;
  118.             }//end if
  119.             
  120.         }//end if
  121.             
  122.         // compute the end length of the segment. If the contour surpasses the segment then end length is segment length
  123.         if (bdr->startedSegment) {
  124.         
  125.             if (!inSegment) d1 = 0.0;
  126.             if (bdr->endLengths[bdr->currentDashSegment] <= (bdr->currentLength + segmentLength) )
  127.                 d2 = bdr->endLengths[bdr->currentDashSegment] - bdr->currentLength;
  128.             else
  129.                 d2 = segmentLength;
  130.                 
  131.             inSegment = true;
  132.         
  133.         }//end if
  134.         
  135.         if (inSegment) {
  136.                     
  137.             // now compute the subcurve from distances d1 through d2.
  138.             DetermineSubCurve(&aCurve, d1, d2, &subCurve);
  139.             CurveToPathsStructure(&subCurve, subPath);            
  140.             
  141.             // Now add the subcurve to the dashed shape we are building.
  142.             if (startContour)
  143.                 GXSetPathParts(bdr->bendDashShape, 0, 0, subPath, gxBreakLeftEdit);
  144.             else
  145.                 GXSetPathParts(bdr->bendDashShape, 0, 0, subPath, gxBreakNeitherEdit);
  146.                             
  147.             // now see if we should go to the next contour of the dash shape or we're done.
  148.             if (bdr->endLengths[bdr->currentDashSegment] <= (bdr->currentLength + segmentLength) ) {
  149.             
  150.                 bdr->startedSegment = false;
  151.                 bdr->currentDashSegment += 1;
  152.                 if (bdr->currentDashSegment >= bdr->dashSegmentCount)        // we've exausted dash segments. 
  153.                     result = true;
  154.             
  155.             }//end if
  156.             
  157.         }//endif
  158.  
  159.         /* update our state */
  160.         
  161.         bdr->currentPoint.x = aCurve.last.x;
  162.         bdr->currentPoint.y = aCurve.last.y;
  163.         bdr->currentLength += segmentLength;
  164.         
  165.         return(result);
  166.     
  167.     }//BendDahsCurveto
  168.     
  169.     
  170. Boolean BendDashLineto(gxPoint *p, TBendDashRecord *bdr);
  171. Boolean BendDashLineto(gxPoint *p, TBendDashRecord *bdr)
  172.     {
  173.         gxLine            aLine;
  174.         Boolean            stop;
  175.         
  176.         aLine.first.x = bdr->currentPoint.x;
  177.         aLine.first.y = bdr->currentPoint.y;
  178.         aLine.last.x = p->x;
  179.         aLine.last.y = p->y;
  180.  
  181.         /* For now, treat the line like a curve so we share the code, not as efficient, but simpler */
  182.         {
  183.             gxPoint        curvep[3];
  184.             curvep[0].x = aLine.first.x;
  185.             curvep[0].y = aLine.first.y;
  186.             
  187.             curvep[1].x = (aLine.first.x + aLine.last.x) / 2;
  188.             curvep[1].y = (aLine.first.y + aLine.last.y) / 2;
  189.  
  190.             curvep[2].x = aLine.last.x;
  191.             curvep[2].y = aLine.last.y;
  192.             
  193.             stop = BendDashCurveto(curvep, bdr);
  194.             
  195.         }
  196.  
  197.         bdr->currentPoint.x = p->x;
  198.         bdr->currentPoint.y = p->y;
  199.         
  200.         return(stop);
  201.     
  202.     }//BendDashLineto
  203.  
  204.  
  205. Boolean BendDashClosepath(TBendDashRecord *bdr);
  206. Boolean BendDashClosepath(TBendDashRecord *bdr)
  207.     {
  208.         Boolean            stop;
  209.         stop = BendDashLineto(&(bdr->firstPoint), bdr);        // just do a line from current to first.
  210.         return(stop);
  211.     
  212.     }//BendDashClosepath
  213.  
  214. Boolean BendDashMoveto(gxPoint *p, TBendDashRecord *bdr);
  215. Boolean BendDashMoveto(gxPoint *p, TBendDashRecord *bdr)
  216.     {
  217.         bdr->currentPoint.x = p->x;
  218.         bdr->currentPoint.y = p->y;
  219.         bdr->firstPoint.x = p->x;
  220.         bdr->firstPoint.y = p->y;
  221.         bdr->currentLength = 0.0;
  222.         bdr->currentDashSegment = 0;
  223.         bdr->startedSegment = false;
  224.     
  225.         return(false);
  226.     
  227.     }//BendDashMoveto
  228.  
  229.  
  230. /****
  231.     Client routine for PathWalker library that will do bend dashing
  232. *****/
  233. void ApplyOneBendDash(gxShape theShape, gxDashRecord *theDash);
  234. void ApplyOneBendDash(gxShape theShape, gxDashRecord *theDash)
  235.     {
  236.         TBendDashRecord        bdr;
  237.         long                            idx;
  238.         gxRectangle                dashSegRect;
  239.         
  240.         bdr.bendDashShape = GXNewShape(gxPathType);            // The result will always be a path.
  241.         
  242.         // allocate memory for length arrays; do it with one allocation split in half.
  243.         bdr.dashSegmentCount = GXCountShapeContours(theDash->dash);
  244.         bdr.startLengths = (double*)NewPtr(2 * bdr.dashSegmentCount * sizeof(double));
  245.         bdr.endLengths = bdr.startLengths + bdr.dashSegmentCount;
  246.         require_action(bdr.startLengths, failed_alloc1, GXPostGraphicsError(out_of_memory););
  247.  
  248.         // Load length arrays. values based on horizontal bounds of contour.
  249.         for (idx = 0; idx < bdr.dashSegmentCount; ++idx) {
  250.         
  251.             GXGetShapeBounds(theDash->dash, idx + 1, &dashSegRect);
  252.             bdr.startLengths[idx] = FixedToDouble(dashSegRect.left);
  253.             bdr.endLengths[idx] = FixedToDouble(dashSegRect.right);
  254.         
  255.         }//end for
  256.         
  257.         // Go for it.
  258.         (void)ShapeWalker( theShape, BendDashMoveto, BendDashLineto, BendDashCurveto, BendDashClosepath, &bdr);
  259.  
  260.         // dispose of allocations.
  261.         DisposePtr((Ptr)bdr.startLengths);
  262.         
  263.         // Move the bent shape into target.
  264.         GXCopyToShape(theShape, bdr.bendDashShape);
  265.         GXSetShapeFill(theShape, gxFrameFill);            // Bend dashing is always opened frame.
  266.  
  267. failed_alloc1:
  268.     
  269.         GXDisposeShape(bdr.bendDashShape);
  270.     
  271.     }//ApplyOneBendDash
  272.     
  273.     
  274.     
  275.     
  276.     
  277. /********************************
  278.  
  279.     ApplyOneBreakDash:
  280.     
  281.     This is my own version of PrimitiveShape for the case of
  282.     a break dash where the dash shape is a polygon.  (see rules below)
  283.     
  284.     This is because there seem to be inaccuracies in GX dashing for this
  285.     case which cause the text positioning to look bad when the dashing
  286.     is used to determine CurveLayout.
  287.     
  288.     Since We know the advance is zero, we only have to repeat the dash once.
  289.     This makes it easy.
  290.  
  291. ********************************/
  292. void ApplyOneBreakDash(gxShape theShape, gxDashRecord *theDash);
  293. void ApplyOneBreakDash(gxShape theShape, gxDashRecord *theDash)
  294.     {
  295.         Ptr                    polyWalker;
  296.         long                numContours;
  297.         long                pointsInContour;
  298.         long                idx;
  299.         gxPoint            contourCenter;
  300.         Fixed                shapeLength;
  301.         Fixed                distance;
  302.         gxPoint            location, tangent;
  303.         Fixed                pen;
  304.         gxMapping        contourMapping, aMapping;
  305.         wide                wLength;
  306.                 
  307.         AccurateGetShapeLength(theShape, 0, &wLength);    
  308.         shapeLength = (Fixed)wLength.lo;
  309.  
  310.         pen = ff(1); //GXGetShapePen(theShape);
  311.         
  312.         GXLockShape(theDash->dash);
  313.         polyWalker = GXGetShapeStructure(theDash->dash, nil);        
  314.  
  315.         numContours = *(long*)polyWalker;
  316.         polyWalker += sizeof(long);
  317.         
  318.         /******************************
  319.         
  320.             For each contour, find the tangent point on the curve at 
  321.                 the distance on the curve corresponding to the horizontal center
  322.                 of the contour.
  323.                 Each contour is scaled by the pen thickness about it's horizontal center.
  324.                 Each scaled contour is rotated about its horizontal center (x,0) by
  325.                  at an angle determined by the tangent vector to the curve.
  326.                 Each rotated contour is then moved so its horizontal position (x,0)
  327.                  is at the point along the curve.
  328.         
  329.         ******************************/
  330.         for (idx = 1; idx <= numContours; ++idx) {
  331.         
  332.             pointsInContour = *(long*)polyWalker;
  333.             polyWalker += sizeof(long);
  334.         
  335.             GXGetShapeCenter(theDash->dash, idx, &contourCenter);
  336.             
  337.             /***
  338.                 find out the distance into the curve to put this countour,
  339.                  wrap if necessary
  340.             ***/            
  341.             distance = contourCenter.x;
  342.             if (distance > shapeLength)
  343.                 distance = distance % (shapeLength + 0x00000001);
  344.  
  345.             AccurateShapeLengthToPoint(theShape, 0, distance, &location, &tangent);
  346.  
  347.             /* Make a mapping for the contour based on the tangent information */
  348.                         
  349.             ResetMapping(&contourMapping);            
  350.             
  351.             /* Move Mapping to origin is at horizontal center of cotour */
  352.             
  353.             MoveMapping(&contourMapping, -contourCenter.x, 0);
  354.  
  355.             /* Scale the Mapping by the pen in the Y direction */
  356.  
  357.             //ScaleMapping(&contourMapping, 0, pen, -contourCenter.x, 0);
  358.                         
  359.             /* Now rotate the mapping by the tangent vector */             
  360.             ResetMapping(&aMapping);
  361.             aMapping.map[0][0] =  tangent.x;
  362.             aMapping.map[1][0] =  -tangent.y;
  363.             aMapping.map[0][1] =  tangent.y;
  364.             aMapping.map[1][1] =  tangent.x;
  365.             MapMapping(&contourMapping, &aMapping);
  366.             MoveMapping(&contourMapping, contourCenter.x, 0);                    // move back to original locaton 
  367.             
  368.             /* Now translate mapping so contours horizontal center will be at point on curve */
  369.             
  370.             MoveMapping(&contourMapping, location.x - contourCenter.x, location.y );
  371.  
  372.             /* Map the points in the contour */
  373.             
  374.             MapPoints(&contourMapping, pointsInContour, (gxPoint*)polyWalker);
  375.             
  376.             /* Move walker to next contour */
  377.             
  378.             polyWalker += pointsInContour * sizeof(gxPoint);
  379.         
  380.         }//end for
  381.         
  382.         GXUnlockShape(theDash->dash);
  383.         
  384.         /* The dash now represents the input shape dashed with it, so copy the result into the input shape */
  385.         
  386.         GXCopyToShape(theShape, theDash->dash);
  387.         
  388.         return;
  389.     
  390.     }//ApplyOneBreakDash
  391.     
  392.     
  393. /****************************
  394.  
  395.     AccuratePrimitiveShape:
  396.     
  397.     This routine replaces GXPrimitiveShape.  This
  398.     is because of the inaccuracies in the GX internal
  399.     dashing code which misplaces dash contours slightly,
  400.     sometimes.
  401.     
  402.     It only works for the kind of dashing that
  403.     we are using for finding CurveLayout glyph
  404.     positions. (see below).  The case is:
  405.     
  406.         •    A framed shape with a dash
  407.         • Dash is a polygon or glyph
  408.         • Pen of 1.0 (or pen of zero with bend-dash)
  409.         • Dash scale is 1.0
  410.         • Dash advance is 0.0
  411.         • Dash phase is 0.0
  412.         • Break or bend dash attributes
  413.  
  414.  
  415. ****************************/
  416. void AccuratePrimitiveShape(gxShape theShape);
  417. void AccuratePrimitiveShape(gxShape theShape)
  418.     {
  419.         gxShapeFill            theFill;
  420.         gxDashRecord        theDash;
  421.         gxShapeType            theType;
  422.         Fixed                        thePen;
  423.         Boolean                    useGXDashing = false;
  424.                 
  425.         /* if it is not a framed shape, do normal primitve and return */
  426.                 
  427.         theFill = GXGetShapeFill(theShape);
  428.         if ( (theFill != gxClosedFrameFill) && (theFill != gxOpenFrameFill) ) {
  429.         
  430.             GXPrimitiveShape(theShape);
  431.             return;
  432.             
  433.         }//end if    
  434.         
  435.         
  436.         /* if it does not have a dash do normal primitve and return */
  437.         
  438.         GXGetShapeDash(theShape, &theDash);
  439.  
  440.         if (theDash.dash == nil) {        
  441.         
  442.             GXPrimitiveShape(theShape);
  443.             return;
  444.             
  445.         }//end if    
  446.         
  447.         /* If the pen is not 1 and it isn't zero with bendashing, do normal primitive */
  448.         thePen = GXGetShapePen(theShape);
  449.         
  450.         if ((thePen != ff(1) && ( (thePen != 0) && (theDash.attributes & gxBendDash) ) ) ) {
  451.         
  452.             GXPrimitiveShape(theShape);
  453.             GXDisposeShape(theDash.dash);
  454.             return;
  455.             
  456.         }//end if
  457.                 
  458.         /* if the dash ins't a polygon do normal primitve and return */
  459.         theType = GXGetShapeType(theDash.dash);
  460.         if (theType != gxPolygonType) {        
  461.         
  462.             GXPrimitiveShape(theShape);
  463.             GXDisposeShape(theDash.dash);
  464.             return;
  465.             
  466.         }//end if    
  467.         
  468.         useGXDashing = false;
  469.         if ( theDash.phase != 0 )
  470.             useGXDashing = true;
  471.         
  472.         if ( theDash.advance != 0)
  473.             useGXDashing = true;
  474.         
  475.         if ( theDash.scale != ff(1) )
  476.             useGXDashing = true;
  477.         
  478.         if ( !(theDash.attributes & gxBreakDash ) && !(theDash.attributes & gxBendDash))
  479.             useGXDashing = true;
  480.  
  481.         if (useGXDashing) {
  482.                 
  483.             GXPrimitiveShape(theShape);
  484.             GXDisposeShape(theDash.dash);
  485.             return;
  486.             
  487.         }//end if    
  488.  
  489.         /* if we got to here, then call our dashing code */
  490.                 
  491.         if ( theDash.attributes & gxBreakDash )
  492.             ApplyOneBreakDash(theShape, &theDash);
  493.         else if (theDash.attributes & gxBendDash )
  494.             ApplyOneBendDash(theShape, &theDash);
  495.             
  496.             
  497.         GXDisposeShape(theDash.dash);
  498.         
  499.         return;        
  500.     
  501.     }//AccuratePrimitiveShape
  502.     
  503.     
  504.